package javaInterview;

import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示在 Java 中控制多个线程执行顺序的 8 种方法
 * @author Cyxan
 * @version 1.0
 */
public class ThreadOrderControl {

    public static void main(String[] args) throws Exception {
        System.out.println("========== 1. CompletableFuture 方式 ==========");
        testCompletableFuture();
        Thread.sleep(1000);

        System.out.println("\n========== 2. synchronized + wait/notify 方式 ==========");
        testSynchronizedWaitNotify();
        Thread.sleep(1000);

        System.out.println("\n========== 3. ReentrantLock + Condition 方式 ==========");
        testReentrantLockCondition();
        Thread.sleep(1000);

        System.out.println("\n========== 4. Thread.join() 方式 ==========");
        testThreadJoin();
        Thread.sleep(1000);

        System.out.println("\n========== 5. CountDownLatch 方式 ==========");
        testCountDownLatch();
        Thread.sleep(1000);

        System.out.println("\n========== 6. CyclicBarrier 方式 ==========");
        testCyclicBarrier();
        Thread.sleep(1000);

        System.out.println("\n========== 7. Semaphore 方式 ==========");
        testSemaphore();
        Thread.sleep(1000);

        System.out.println("\n========== 8. 单线程线程池方式 ==========");
        testSingleThreadPool();
    }

    /**
     * 1. 使用 CompletableFuture 控制执行顺序
     * 原理：通过 thenRun 方法链式调用，后一个任务会等待前一个任务完成
     */
    private static void testCompletableFuture() throws Exception {
        CompletableFuture.runAsync(() -> {
            System.out.println("任务1执行 - " + Thread.currentThread().getName());
        }).thenRun(() -> {
            System.out.println("任务2执行 - " + Thread.currentThread().getName());
        }).thenRun(() -> {
            System.out.println("任务3执行 - " + Thread.currentThread().getName());
        }).thenRun(() -> {
            System.out.println("任务4执行 - " + Thread.currentThread().getName());
        }).get(); // 阻塞等待所有任务完成
    }

    /**
     * 2. 使用 synchronized + wait/notify 控制执行顺序
     * 原理：通过共享锁对象和标志位，线程检查标志位决定是执行还是等待
     */
    private static void testSynchronizedWaitNotify() throws InterruptedException {
        Object lock = new Object();
        // 标志位：1表示T1执行，2表示T2执行，3表示T3执行
        int[] flag = {1};

        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                while (flag[0] != 1) {
                    try {
                        lock.wait(); // 不是T1的回合，等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("任务1执行 - " + Thread.currentThread().getName());
                flag[0] = 2; // 切换到T2
                lock.notifyAll(); // 唤醒所有等待线程
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                while (flag[0] != 2) {
                    try {
                        lock.wait(); // 不是T2的回合，等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("任务2执行 - " + Thread.currentThread().getName());
                flag[0] = 3; // 切换到T3
                lock.notifyAll(); // 唤醒所有等待线程
            }
        });

        Thread t3 = new Thread(() -> {
            synchronized (lock) {
                while (flag[0] != 3) {
                    try {
                        lock.wait(); // 不是T3的回合，等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("任务3执行 - " + Thread.currentThread().getName());
            }
        });

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 3. 使用 ReentrantLock + Condition 控制执行顺序
     * 原理：类似 wait/notify，但更灵活，每个任务有自己的 Condition
     */
    private static void testReentrantLockCondition() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        Condition condition3 = lock.newCondition();
        int[] flag = {1};

        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                while (flag[0] != 1) {
                    condition1.await(); // 等待被唤醒
                }
                System.out.println("任务1执行 - " + Thread.currentThread().getName());
                flag[0] = 2;
                condition2.signal(); // 唤醒T2
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread t2 = new Thread(() -> {
            lock.lock();
            try {
                while (flag[0] != 2) {
                    condition2.await(); // 等待被唤醒
                }
                System.out.println("任务2执行 - " + Thread.currentThread().getName());
                flag[0] = 3;
                condition3.signal(); // 唤醒T3
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread t3 = new Thread(() -> {
            lock.lock();
            try {
                while (flag[0] != 3) {
                    condition3.await(); // 等待被唤醒
                }
                System.out.println("任务3执行 - " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 4. 使用 Thread.join() 控制执行顺序
     * 原理：在主线程中依次启动线程并调用 join()，确保前一个线程执行完再启动下一个
     */
    private static void testThreadJoin() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("任务1执行 - " + Thread.currentThread().getName());
        });

        Thread t2 = new Thread(() -> {
            System.out.println("任务2执行 - " + Thread.currentThread().getName());
        });

        Thread t3 = new Thread(() -> {
            System.out.println("任务3执行 - " + Thread.currentThread().getName());
        });

        t1.start();
        t1.join(); // 等待T1执行完

        t2.start();
        t2.join(); // 等待T2执行完

        t3.start();
        t3.join(); // 等待T3执行完
    }

    /**
     * 5. 使用 CountDownLatch 控制执行顺序
     * 原理：每个线程持有前一个任务的 CountDownLatch，等待前一个任务完成后再执行
     */
    private static void testCountDownLatch() throws InterruptedException {
        // latch1: 控制T2的启动，latch2: 控制T3的启动
        CountDownLatch latch1 = new CountDownLatch(1);
        CountDownLatch latch2 = new CountDownLatch(1);

        Thread t1 = new Thread(() -> {
            System.out.println("任务1执行 - " + Thread.currentThread().getName());
            latch1.countDown(); // T1完成，通知T2可以开始
        });

        Thread t2 = new Thread(() -> {
            try {
                latch1.await(); // 等待T1完成
                System.out.println("任务2执行 - " + Thread.currentThread().getName());
                latch2.countDown(); // T2完成，通知T3可以开始
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() -> {
            try {
                latch2.await(); // 等待T2完成
                System.out.println("任务3执行 - " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 6. 使用 CyclicBarrier 控制执行顺序
     * 原理：每个阶段设置一个屏障，所有线程到达屏障后才能继续执行下一阶段
     */
    private static void testCyclicBarrier() throws InterruptedException {
        // 3个线程都到达屏障后才继续
        CyclicBarrier barrier1 = new CyclicBarrier(1, () -> {
            System.out.println("任务1执行 - barrier");
        });
        CyclicBarrier barrier2 = new CyclicBarrier(1, () -> {
            System.out.println("任务2执行 - barrier");
        });
        CyclicBarrier barrier3 = new CyclicBarrier(1, () -> {
            System.out.println("任务3执行 - barrier");
        });

        Thread t = new Thread(() -> {
            try {
                barrier1.await(); // 等待所有线程到达
                barrier2.await(); // 等待所有线程到达
                barrier3.await(); // 等待所有线程到达
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        t.start();
        t.join();
    }

    /**
     * 7. 使用 Semaphore 控制执行顺序
     * 原理：信号量初始为0，每个线程先acquire再release，实现顺序控制
     */
    private static void testSemaphore() throws InterruptedException {
        // 初始许可为1，表示T1可以直接执行
        Semaphore semaphore1 = new Semaphore(1);
        // 初始许可为0，T2需要等待T1释放许可
        Semaphore semaphore2 = new Semaphore(0);
        // 初始许可为0，T3需要等待T2释放许可
        Semaphore semaphore3 = new Semaphore(0);

        Thread t1 = new Thread(() -> {
            try {
                semaphore1.acquire(); // 获取许可（初始就有）
                System.out.println("任务1执行 - " + Thread.currentThread().getName());
                semaphore2.release(); // 释放许可，让T2可以执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                semaphore2.acquire(); // 等待T1释放许可
                System.out.println("任务2执行 - " + Thread.currentThread().getName());
                semaphore3.release(); // 释放许可，让T3可以执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() -> {
            try {
                semaphore3.acquire(); // 等待T2释放许可
                System.out.println("任务3执行 - " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 8. 使用单线程线程池控制执行顺序
     * 原理：线程池只有一个线程，按提交顺序依次执行任务
     */
    private static void testSingleThreadPool() throws InterruptedException {
        // 创建只有一个线程的线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 按顺序提交任务，会按顺序执行
        executor.submit(() -> {
            System.out.println("任务1执行 - " + Thread.currentThread().getName());
        });

        executor.submit(() -> {
            System.out.println("任务2执行 - " + Thread.currentThread().getName());
        });

        executor.submit(() -> {
            System.out.println("任务3执行 - " + Thread.currentThread().getName());
        });

        // 关闭线程池
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
    }
}

